package decision

import (
	"os"
	"path/filepath"
	"strings"
	"testing"
)

// TestPromptReloadEndToEnd 端到端测试：验证从文件修改到决策引擎使用的完整流程
func TestPromptReloadEndToEnd(t *testing.T) {
	// 保存原始的 promptsDir
	originalDir := promptsDir
	defer func() {
		promptsDir = originalDir
		// 恢复原始模板
		globalPromptManager.ReloadTemplates(originalDir)
	}()

	// 创建临时目录模拟 prompts/ 目录
	tempDir := t.TempDir()
	promptsDir = tempDir

	// 步骤1: 创建初始 prompt 文件
	initialContent := "# 初始交易策略\n你是一个保守的交易AI。"
	if err := os.WriteFile(filepath.Join(tempDir, "test_strategy.txt"), []byte(initialContent), 0644); err != nil {
		t.Fatalf("创建初始文件失败: %v", err)
	}

	// 步骤2: 首次加载（模拟系统启动）
	if err := ReloadPromptTemplates(); err != nil {
		t.Fatalf("首次加载失败: %v", err)
	}

	// 步骤3: 验证初始内容
	template, err := GetPromptTemplate("test_strategy")
	if err != nil {
		t.Fatalf("获取初始模板失败: %v", err)
	}
	if template.Content != initialContent {
		t.Errorf("初始内容不匹配\n期望: %s\n实际: %s", initialContent, template.Content)
	}

	// 步骤4: 使用 buildSystemPrompt 验证模板被正确使用
	systemPrompt := buildSystemPrompt(10000.0, 10, 5, "test_strategy", 12.0)
	if !strings.Contains(systemPrompt, initialContent) {
		t.Errorf("buildSystemPrompt 未包含模板内容\n生成的 prompt:\n%s", systemPrompt)
	}

	// 步骤5: 模拟用户修改文件（这是用户在硬盘上修改 prompt）
	updatedContent := "# 更新的交易策略\n你是一个激进的交易AI，追求高风险高收益。"
	if err := os.WriteFile(filepath.Join(tempDir, "test_strategy.txt"), []byte(updatedContent), 0644); err != nil {
		t.Fatalf("更新文件失败: %v", err)
	}

	// 步骤6: 模拟交易员启动时调用 ReloadPromptTemplates()
	t.Log("模拟交易员启动，调用 ReloadPromptTemplates()...")
	if err := ReloadPromptTemplates(); err != nil {
		t.Fatalf("重新加载失败: %v", err)
	}

	// 步骤7: 验证新内容已生效
	reloadedTemplate, err := GetPromptTemplate("test_strategy")
	if err != nil {
		t.Fatalf("获取重新加载的模板失败: %v", err)
	}
	if reloadedTemplate.Content != updatedContent {
		t.Errorf("重新加载后内容不匹配\n期望: %s\n实际: %s", updatedContent, reloadedTemplate.Content)
	}

	// 步骤8: 验证 buildSystemPrompt 使用了新内容
	newSystemPrompt := buildSystemPrompt(10000.0, 10, 5, "test_strategy", 12.0)
	if !strings.Contains(newSystemPrompt, updatedContent) {
		t.Errorf("buildSystemPrompt 未包含更新后的模板内容\n生成的 prompt:\n%s", newSystemPrompt)
	}

	// 步骤9: 验证旧内容不再存在
	if strings.Contains(newSystemPrompt, "保守的交易AI") {
		t.Errorf("buildSystemPrompt 仍包含旧的模板内容")
	}

	t.Log("✅ 端到端测试通过：文件修改 -> 重新加载 -> 决策引擎使用新内容")
}

// TestPromptReloadWithCustomPrompt 测试自定义 prompt 与模板重新加载的交互
func TestPromptReloadWithCustomPrompt(t *testing.T) {
	// 保存原始的 promptsDir
	originalDir := promptsDir
	defer func() {
		promptsDir = originalDir
		globalPromptManager.ReloadTemplates(originalDir)
	}()

	// 创建临时目录
	tempDir := t.TempDir()
	promptsDir = tempDir

	// 创建基础模板
	baseContent := "基础策略：稳健交易"
	if err := os.WriteFile(filepath.Join(tempDir, "base.txt"), []byte(baseContent), 0644); err != nil {
		t.Fatalf("创建文件失败: %v", err)
	}

	// 加载模板
	if err := ReloadPromptTemplates(); err != nil {
		t.Fatalf("加载失败: %v", err)
	}

	// 测试1: 基础模板 + 自定义 prompt（不覆盖）
	customPrompt := "个性化规则：只交易 BTC"
	result := buildSystemPromptWithCustom(10000.0, 10, 5, customPrompt, false, "base", 12.0)
	if !strings.Contains(result, baseContent) {
		t.Errorf("未包含基础模板内容")
	}
	if !strings.Contains(result, customPrompt) {
		t.Errorf("未包含自定义 prompt")
	}

	// 测试2: 覆盖基础 prompt
	result = buildSystemPromptWithCustom(10000.0, 10, 5, customPrompt, true, "base", 12.0)
	if strings.Contains(result, baseContent) {
		t.Errorf("覆盖模式下仍包含基础模板内容")
	}
	if !strings.Contains(result, customPrompt) {
		t.Errorf("覆盖模式下未包含自定义 prompt")
	}

	// 测试3: 重新加载后效果
	updatedBase := "更新的基础策略：激进交易"
	if err := os.WriteFile(filepath.Join(tempDir, "base.txt"), []byte(updatedBase), 0644); err != nil {
		t.Fatalf("更新文件失败: %v", err)
	}

	if err := ReloadPromptTemplates(); err != nil {
		t.Fatalf("重新加载失败: %v", err)
	}

	result = buildSystemPromptWithCustom(10000.0, 10, 5, customPrompt, false, "base", 12.0)
	if !strings.Contains(result, updatedBase) {
		t.Errorf("重新加载后未包含更新的基础模板内容")
	}
	if strings.Contains(result, baseContent) {
		t.Errorf("重新加载后仍包含旧的基础模板内容")
	}
}

// TestPromptReloadFallback 测试模板不存在时的致命错误机制
func TestPromptReloadFallback(t *testing.T) {
	// 保存原始的 promptsDir 和 fatalFunc
	originalDir := promptsDir
	originalFatal := fatalFunc
	defer func() {
		promptsDir = originalDir
		fatalFunc = originalFatal
		globalPromptManager.ReloadTemplates(originalDir)
	}()

	// 创建临时目录
	tempDir := t.TempDir()
	promptsDir = tempDir

	// 只创建 default 模板
	defaultContent := "默认策略"
	if err := os.WriteFile(filepath.Join(tempDir, "default.txt"), []byte(defaultContent), 0644); err != nil {
		t.Fatalf("创建文件失败: %v", err)
	}

	if err := ReloadPromptTemplates(); err != nil {
		t.Fatalf("加载失败: %v", err)
	}

	// 测试1: 请求不存在的模板，应该触发 fatal
	var fatalCalled bool
	fatalFunc = func(format string, v ...interface{}) {
		fatalCalled = true
		panic("fatal called")
	}

	defer func() {
		if r := recover(); r != nil {
			if !fatalCalled {
				t.Error("Expected fatalFunc to be called for nonexistent template")
			}
		} else {
			t.Error("Expected panic from fatalFunc for nonexistent template")
		}
	}()

	// 这将触发 fatal
	buildSystemPrompt(10000.0, 10, 5, "nonexistent", 12.0)
}

// TestConcurrentPromptReload 测试并发场景下的 prompt 重新加载
func TestConcurrentPromptReload(t *testing.T) {
	// 保存原始的 promptsDir
	originalDir := promptsDir
	defer func() {
		promptsDir = originalDir
		globalPromptManager.ReloadTemplates(originalDir)
	}()

	// 创建临时目录
	tempDir := t.TempDir()
	promptsDir = tempDir

	// 创建测试文件
	if err := os.WriteFile(filepath.Join(tempDir, "test.txt"), []byte("测试内容"), 0644); err != nil {
		t.Fatalf("创建文件失败: %v", err)
	}

	if err := ReloadPromptTemplates(); err != nil {
		t.Fatalf("初始加载失败: %v", err)
	}

	// 并发测试：同时读取和重新加载
	done := make(chan bool)

	// 启动多个读取 goroutine
	for i := 0; i < 10; i++ {
		go func() {
			for j := 0; j < 100; j++ {
				_, _ = GetPromptTemplate("test")
			}
			done <- true
		}()
	}

	// 启动多个重新加载 goroutine
	for i := 0; i < 3; i++ {
		go func() {
			for j := 0; j < 10; j++ {
				_ = ReloadPromptTemplates()
			}
			done <- true
		}()
	}

	// 等待所有 goroutine 完成
	for i := 0; i < 13; i++ {
		<-done
	}

	// 验证最终状态正确
	template, err := GetPromptTemplate("test")
	if err != nil {
		t.Errorf("并发测试后获取模板失败: %v", err)
	}
	if template.Content != "测试内容" {
		t.Errorf("并发测试后模板内容错误: %s", template.Content)
	}

	t.Log("✅ 并发测试通过：多个 goroutine 同时读取和重新加载模板，无数据竞争")
}
